﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Globalization;
using Moq;
using JTLeigh.Commerce.Spencer.Core.Common;

namespace JTLeigh.Commerce.Spencer.Core.Testing.Mocks.Data
{

	/// <summary>
	/// <see langword="abstract"/> base class for mock database commands.
	/// </summary>
	/// <remarks>
	/// <para>
	/// <see cref="BaseMockDatabaseCommand"/> ensures that the command text is set to the specified 
	/// schema name and command name, the command type is set to stored procedure, the specified 
	/// number of parameters are allocated and added, and the command is disposed.
	/// </para>
	/// <para>
	/// Eventually this class should ensure that the added parameters have the correct names and
	/// types.
	/// </para>
	/// </remarks>
	[CLSCompliant(false)]
	public abstract class BaseMockDatabaseCommand : Mock<IDbCommand>
	{

		#region Fields

		private string _commandText;
		private int _expectedParameterCount;
		private int _createdParameterCount;
		private int _addedParameterCount;

		#endregion // Fields

		#region Constructors

		/// <summary>
		/// Initializes a new instance of the <see cref="BaseMockDatabaseCommand"/> class with the
		/// specified command name and parameter count. 
		/// </summary>
		/// <param name="schemaName">The schema name.</param>
		/// <param name="commandName">The command name.</param>
		/// <param name="parameterCount">The number of parameters that should be passed to the
		/// command.</param>
		protected BaseMockDatabaseCommand(string schemaName, string commandName, int parameterCount)
			: base(MockBehavior.Strict)
		{
			Safety.AssertArgumentNotNull("schemaName", schemaName);
			Safety.AssertArgumentNotNull("commandName", commandName);

			_commandText = commandName;
			_expectedParameterCount = parameterCount;

			ExpectSet(x => x.CommandText, String.Concat("[", schemaName, "].[", commandName, "]")).AtMostOnce();
			ExpectSet(x => x.CommandType, CommandType.StoredProcedure).AtMostOnce();
			Expect(x => x.CreateParameter()).Returns(new SqlParameter()).Callback(() => _createdParameterCount++).AtMost(parameterCount);
			Expect(x => x.Parameters.Add(It.IsAny<IDataParameter>())).Returns(() => _addedParameterCount).Callback(() => _addedParameterCount++).AtMost(parameterCount);
			Expect(x => x.Dispose()).AtMostOnce();
		}

		#endregion // Constructors

		#region Methods

		/// <summary>
		/// Verifies the mock's expectations.
		/// </summary>
		public override void VerifyAll()
		{
			base.VerifyAll();
			VerifyParameterCounts();
		}

		/// <summary>
		/// Verifies the mock's expectations.
		/// </summary>
		public override void Verify()
		{
			base.Verify();
			VerifyParameterCounts();
		}

		#endregion // Methods

		#region Implementation

		private void VerifyParameterCounts()
		{
			if (_createdParameterCount != _expectedParameterCount)
			{
				throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Command {0} expected {1} parameter(s) but {2} parameter(s) were created.", _commandText, _expectedParameterCount, _createdParameterCount));
			}
			if (_addedParameterCount != _expectedParameterCount)
			{
				throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Command {0} expected {1} parameter(s) but {2} parameter(s) were added.", _commandText, _expectedParameterCount, _addedParameterCount));
			}
		}

		#endregion // Implementation

	}

}
